package log

import (
	"context"
	"sync"

	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
)

// Logger 抽象了日志记录能力。
type Logger interface {
	Debug(msg string, fields ...Field)
	Debugf(foramt string, v ...any)
	Debugw(msg string, kv ...any)

	Info(msg string, fields ...Field)
	Infof(foramt string, v ...any)
	Infow(msg string, kv ...any)

	Warn(msg string, fields ...Field)
	Warnf(foramt string, v ...any)
	Warnw(msg string, kv ...any)

	Error(msg string, fields ...Field)
	Errorf(foramt string, v ...any)
	Errorw(msg string, kv ...any)

	// DPanic 意为开发阶段的 panic ，用于捕获可恢复但不应该发生的错误。开发模式
	// 下行为与 panic 类似，将在打印栈追踪信息后退出，非开发模式下行为与 error
	// 类似，仅记录问题。
	DPanic(msg string, fields ...Field)
	DPanicf(foramt string, v ...any)
	DPanicw(msg string, kv ...any)

	Panic(msg string, fields ...Field)
	Panicf(foramt string, v ...any)
	Panicw(msg string, kv ...any)

	Fatal(msg string, fields ...Field)
	Fatalf(foramt string, v ...any)
	Fatalw(msg string, kv ...any)

	// WithValues 返回附加键值对的 logger 。
	WithValues(kv ...any) Logger
	// WithName 返回命名的 logger 。
	WithName(name string) Logger
	// WithContext 返回附加 logger 的上下文。
	WithContext(ctx context.Context) context.Context

	// Flush 刷新缓冲区，确保应用退出前调用。
	Flush()
}

// [顶层变量声明](https://github.com/xxjwxc/uber_go_guide_cn#%E9%A1%B6%E5%B1%82%E5%8F%98%E9%87%8F%E5%A3%B0%E6%98%8E)
// [对于未导出的顶层常量和变量，使用_作为前缀](https://github.com/xxjwxc/uber_go_guide_cn#%E5%AF%B9%E4%BA%8E%E6%9C%AA%E5%AF%BC%E5%87%BA%E7%9A%84%E9%A1%B6%E5%B1%82%E5%B8%B8%E9%87%8F%E5%92%8C%E5%8F%98%E9%87%8F%E4%BD%BF%E7%94%A8_%E4%BD%9C%E4%B8%BA%E5%89%8D%E7%BC%80)
// [零值 Mutex 是有效的](https://github.com/xxjwxc/uber_go_guide_cn#%E9%9B%B6%E5%80%BC-mutex-%E6%98%AF%E6%9C%89%E6%95%88%E7%9A%84)

var (
	// 注意：虽然在传入 nil 时仍然会返回通过默认配置创建的 logger ，但它是 New 函
	// 数的兜底逻辑，这里明确需要默认配置，因此不应依赖 New 函数的行为。

	// _defaultLogger 导入包时通过默认配置创建的 logger ，导入后可使用指定配置重
	// 新创建。
	_defaultLogger = New(NewOptions())
	mu             sync.Mutex
)

// Init 使用指定的配置重新创建 logger 。
func Init(opts *Options) {
	mu.Lock()
	defer mu.Unlock()

	_defaultLogger = New(opts)
}

// New 创建 logger 。
func New(opts *Options) *logger {
	if opts == nil {
		opts = NewOptions()
	}

	var unmarshalLevelErr bool
	var level Level
	if err := level.UnmarshalText([]byte(opts.Level)); err != nil {
		unmarshalLevelErr = true
		level = InfoLevel
	}

	encodeLevel := zapcore.CapitalLevelEncoder
	if opts.Format == consoleFormat && opts.EnableColor {
		encodeLevel = zapcore.CapitalColorLevelEncoder
	}

	encoderConfig := zapcore.EncoderConfig{
		MessageKey:     "message",
		LevelKey:       "level",
		TimeKey:        "timestamp",
		NameKey:        "logger",
		CallerKey:      "caller",
		StacktraceKey:  "stacktrace",
		LineEnding:     zapcore.DefaultLineEnding,
		EncodeLevel:    encodeLevel,
		EncodeTime:     timeEncoder,
		EncodeDuration: milliSecondsDurationEncoder,
		EncodeCaller:   zapcore.ShortCallerEncoder,
	}

	loggerConfig := &zap.Config{
		Level:             zap.NewAtomicLevelAt(level),
		Development:       opts.Development,
		DisableCaller:     opts.DisableCaller,
		DisableStacktrace: opts.DisableStacktrace,
		Sampling: &zap.SamplingConfig{
			Initial:    100,
			Thereafter: 100,
		},
		Encoding:         opts.Format,
		EncoderConfig:    encoderConfig,
		OutputPaths:      opts.OutputPaths,
		ErrorOutputPaths: opts.ErrorOutputPaths,
	}

	// [不要使用 panic](https://github.com/xxjwxc/uber_go_guide_cn#%E4%B8%8D%E8%A6%81%E4%BD%BF%E7%94%A8-panic)

	rootLogger, err := loggerConfig.Build(zap.AddCallerSkip(1))
	// logger 创建失败程序崩溃退出。
	if err != nil {
		panic(err)
	}

	logger := &logger{
		// 配置项 Name 未暴露，只能创建未命名的 Logger 。
		logger: rootLogger.Named(opts.Name),
	}

	zap.RedirectStdLog(rootLogger)

	if unmarshalLevelErr {
		logger.Error("log level error, fallback to INFO level")
	}

	return logger
}

func Debug(msg string, fields ...Field) {
	_defaultLogger.logger.Debug(msg, fields...)
}

func Debugf(format string, v ...any) {
	_defaultLogger.logger.Sugar().Debugf(format, v...)
}

func Debugw(msg string, kv ...any) {
	_defaultLogger.logger.Sugar().Debugw(msg, kv...)
}

func Info(msg string, fields ...Field) {
	_defaultLogger.logger.Info(msg, fields...)
}

func Infof(format string, v ...any) {
	_defaultLogger.logger.Sugar().Infof(format, v...)
}

func Infow(msg string, kv ...any) {
	_defaultLogger.logger.Sugar().Infow(msg, kv...)
}

func Warn(msg string, fields ...Field) {
	_defaultLogger.logger.Warn(msg, fields...)
}

func Warnf(format string, v ...any) {
	_defaultLogger.logger.Sugar().Warnf(format, v...)
}

func Warnw(msg string, kv ...any) {
	_defaultLogger.logger.Sugar().Warnw(msg, kv...)
}

func Error(msg string, fields ...Field) {
	_defaultLogger.logger.Error(msg, fields...)
}

func Errorf(format string, v ...any) {
	_defaultLogger.logger.Sugar().Errorf(format, v...)
}

func Errorw(msg string, kv ...any) {
	_defaultLogger.logger.Sugar().Errorw(msg, kv...)
}

func DPanic(msg string, fields ...Field) {
	_defaultLogger.logger.DPanic(msg, fields...)
}

func DPanicf(format string, v ...any) {
	_defaultLogger.logger.Sugar().DPanicf(format, v...)
}

func DPanicw(msg string, kv ...any) {
	_defaultLogger.logger.Sugar().DPanicw(msg, kv...)
}

func Panic(msg string, fields ...Field) {
	_defaultLogger.logger.Panic(msg, fields...)
}

func Panicf(format string, v ...any) {
	_defaultLogger.logger.Sugar().Panicf(format, v...)
}

func Panicw(msg string, kv ...any) {
	_defaultLogger.logger.Sugar().Panicw(msg, kv...)
}

func Fatal(msg string, fields ...Field) {
	_defaultLogger.logger.Fatal(msg, fields...)
}

func Fatalf(format string, v ...any) {
	_defaultLogger.logger.Sugar().Fatalf(format, v...)
}

func Fatalw(msg string, kv ...any) {
	_defaultLogger.logger.Sugar().Fatalw(msg, kv...)
}

// Flush 刷新缓冲区到文件。
func Flush() { _defaultLogger.Flush() }

// WithValues 返回一个携带指定键值对的 Logger 。
func WithValues(kv ...any) Logger { return _defaultLogger.WithValues(kv...) }

// WithName 返回一个命名的 Logger 。
func WithName(name string) Logger { return _defaultLogger.WithName(name) }

// PackZapLogger 包装 *zap.Logger 类型的实例为 Logger 类型的实例。
func PackZapLogger(zapLogger *zap.Logger) Logger {
	return &logger{
		logger: zapLogger,
	}
}

// [结构体中的嵌入](https://github.com/xxjwxc/uber_go_guide_cn#%E7%BB%93%E6%9E%84%E4%BD%93%E4%B8%AD%E7%9A%84%E5%B5%8C%E5%85%A5)

// logger 综合了 sugar 和 unsugar 的实现，类似 Info 的方法使用 unsugar 实现，具
// 备更高的性能，类似 Infof 和 Infow 的方法使用 sugar 实现，因为其内部需要判断类
// 型，所以性能存在劣化，但相对仍然是一个高性能的实现。
type logger struct {
	logger *zap.Logger
}

// [Interface 合理性验证](https://github.com/xxjwxc/uber_go_guide_cn#interface-%E5%90%88%E7%90%86%E6%80%A7%E9%AA%8C%E8%AF%81)

var _ Logger = &logger{}

func (l *logger) Debug(msg string, fields ...Field) {
	l.logger.Debug(msg, fields...)
}

func (l *logger) Debugf(format string, v ...any) {
	l.logger.Sugar().Debugf(format, v...)
}

func (l *logger) Debugw(msg string, kv ...any) {
	l.logger.Sugar().Debugw(msg, kv...)
}

func (l *logger) Info(msg string, fields ...Field) {
	l.logger.Info(msg, fields...)
}

func (l *logger) Infof(format string, v ...any) {
	l.logger.Sugar().Infof(format, v...)
}

func (l *logger) Infow(msg string, kv ...any) {
	l.logger.Sugar().Infow(msg, kv...)
}

func (l *logger) Warn(msg string, fields ...Field) {
	l.logger.Warn(msg, fields...)
}

func (l *logger) Warnf(format string, v ...any) {
	l.logger.Sugar().Warnf(format, v...)
}

func (l *logger) Warnw(msg string, kv ...any) {
	l.logger.Sugar().Warnw(msg, kv...)
}

func (l *logger) Error(msg string, fields ...Field) {
	l.logger.Error(msg, fields...)
}

func (l *logger) Errorf(format string, v ...any) {
	l.logger.Sugar().Errorf(format, v...)
}

func (l *logger) Errorw(msg string, kv ...any) {
	l.logger.Sugar().Errorw(msg, kv...)
}

func (l *logger) DPanic(msg string, fields ...Field) {
	l.logger.DPanic(msg, fields...)
}

func (l *logger) DPanicf(format string, v ...any) {
	l.logger.Sugar().DPanicf(format, v...)
}

func (l *logger) DPanicw(msg string, kv ...any) {
	l.logger.Sugar().DPanicw(msg, kv...)
}

func (l *logger) Panic(msg string, fields ...Field) {
	l.logger.Panic(msg, fields...)
}

func (l *logger) Panicf(format string, v ...any) {
	l.logger.Sugar().Panicf(format, v...)
}

func (l *logger) Panicw(msg string, kv ...any) {
	l.logger.Sugar().Panicw(msg, kv...)
}

func (l *logger) Fatal(msg string, fields ...Field) {
	l.logger.Fatal(msg, fields...)
}

func (l *logger) Fatalf(format string, v ...any) {
	l.logger.Sugar().Fatalf(format, v...)
}

func (l *logger) Fatalw(msg string, kv ...any) {
	l.logger.Sugar().Fatalw(msg, kv...)
}

// 返回一个附加键值对的子 logger 。
func (l *logger) WithValues(kv ...any) Logger {
	newZapLogger := l.logger.With(handleFields(l.logger, kv)...)

	return PackZapLogger(newZapLogger)
}

// 返回一个命名的子 logger 。
func (l *logger) WithName(name string) Logger {
	newZapLogger := l.logger.Named(name)

	return PackZapLogger(newZapLogger)
}

func (l *logger) Flush() {
	_ = l.logger.Sync()
}

// [不要使用 panic](https://github.com/xxjwxc/uber_go_guide_cn#%E4%B8%8D%E8%A6%81%E4%BD%BF%E7%94%A8-panic)

// handleFields 转换弱类型的 []any 序列和强类型的 Field 序列为统一的 Field 序列。
//
// 逻辑参考`go/pkg/mod/go.uber.org/zap@v1.26.0/sugar.go`中的：
// func (s *SugaredLogger) sweetenFields(args []interface{}) []Field ，包装实现
// 中的错误级别与 sweetenFields 实现保持一致。
func handleFields(logger *zap.Logger, kv []any, additionals ...Field) []Field {
	if len(kv) == 0 {
		return additionals
	}

	// [指定容器容量](https://github.com/xxjwxc/uber_go_guide_cn#%E6%8C%87%E5%AE%9A%E5%AE%B9%E5%99%A8%E5%AE%B9%E9%87%8F)

	fields := make([]Field, 0, len(kv)/2+len(additionals))

	for i := 0; i < len(kv); {
		// 键值对中不应包含强类型 。
		if _, ok := kv[i].(Field); ok {
			// Any 方法尽量避免回退到基于反射的实现。
			logger.Error("ignore strongly-typed Zap Field passed to logger:",
				Any("zap field", kv[i]))
			i++
			continue
		}

		// 键值对中不应该出现悬空键，例如键值不成对出现时。
		if i == len(kv)-1 {
			logger.Error("dangling keys passed to logger:",
				Any("ignored key", kv[i]))
			break
		}

		k, v := kv[i], kv[i+1]
		kStr, ok := k.(string)
		// 键应该为字符串类型。
		if !ok {
			logger.Error("non-string key argument passed to logger, ignoring all later arguments:",
				Any("invalid key", k))
			break
		}

		fields = append(fields, Any(kStr, v))
		i += 2
	}

	return append(fields, additionals...)
}
